import type { Message } from '@/types'

export interface CacheConfig {
  maxMessages: number
  maxAge: number // in milliseconds
  maxMemoryUsage: number // in bytes
  cleanupInterval: number // in milliseconds
}

export interface CacheStats {
  totalMessages: number
  totalMemoryUsage: number
  oldestMessage: Date | null
  newestMessage: Date | null
  conversationCount: number
}

export interface CacheEntry {
  message: Message
  timestamp: number
  size: number
}

class MessageCacheManager {
  private cache = new Map<string, Map<string, CacheEntry>>()
  private config: CacheConfig
  private cleanupTimer: number | null = null
  private memoryUsage = 0

  constructor(config: Partial<CacheConfig> = {}) {
    this.config = {
      maxMessages: 1000,
      maxAge: 24 * 60 * 60 * 1000, // 24 hours
      maxMemoryUsage: 50 * 1024 * 1024, // 50MB
      cleanupInterval: 5 * 60 * 1000, // 5 minutes
      ...config
    }

    this.startCleanupTimer()
  }

  // Add message to cache
  addMessage(conversationId: string, message: Message): void {
    if (!this.cache.has(conversationId)) {
      this.cache.set(conversationId, new Map())
    }

    const conversationCache = this.cache.get(conversationId)!
    const messageSize = this.calculateMessageSize(message)
    
    const entry: CacheEntry = {
      message: { ...message },
      timestamp: Date.now(),
      size: messageSize
    }

    // Remove existing message if it exists
    const existingEntry = conversationCache.get(message.messageId)
    if (existingEntry) {
      this.memoryUsage -= existingEntry.size
    }

    conversationCache.set(message.messageId, entry)
    this.memoryUsage += messageSize

    // Check if we need to cleanup
    this.checkAndCleanup()
  }

  // Add multiple messages
  addMessages(conversationId: string, messages: Message[]): void {
    messages.forEach(message => {
      this.addMessage(conversationId, message)
    })
  }

  // Get message from cache
  getMessage(conversationId: string, messageId: string): Message | null {
    const conversationCache = this.cache.get(conversationId)
    if (!conversationCache) return null

    const entry = conversationCache.get(messageId)
    if (!entry) return null

    // Check if message is expired
    if (this.isExpired(entry)) {
      this.removeMessage(conversationId, messageId)
      return null
    }

    return entry.message
  }

  // Get messages for conversation
  getMessages(conversationId: string, limit?: number): Message[] {
    const conversationCache = this.cache.get(conversationId)
    if (!conversationCache) return []

    const messages: Message[] = []
    const entries = Array.from(conversationCache.values())
      .filter(entry => !this.isExpired(entry))
      .sort((a, b) => a.message.sequence - b.message.sequence)

    const messagesToReturn = limit ? entries.slice(-limit) : entries
    
    return messagesToReturn.map(entry => entry.message)
  }

  // Remove message from cache
  removeMessage(conversationId: string, messageId: string): boolean {
    const conversationCache = this.cache.get(conversationId)
    if (!conversationCache) return false

    const entry = conversationCache.get(messageId)
    if (!entry) return false

    conversationCache.delete(messageId)
    this.memoryUsage -= entry.size

    // Remove conversation cache if empty
    if (conversationCache.size === 0) {
      this.cache.delete(conversationId)
    }

    return true
  }

  // Clear conversation cache
  clearConversation(conversationId: string): void {
    const conversationCache = this.cache.get(conversationId)
    if (!conversationCache) return

    // Update memory usage
    conversationCache.forEach(entry => {
      this.memoryUsage -= entry.size
    })

    this.cache.delete(conversationId)
  }

  // Clear all cache
  clearAll(): void {
    this.cache.clear()
    this.memoryUsage = 0
  }

  // Update message in cache
  updateMessage(conversationId: string, messageId: string, updates: Partial<Message>): boolean {
    const conversationCache = this.cache.get(conversationId)
    if (!conversationCache) return false

    const entry = conversationCache.get(messageId)
    if (!entry) return false

    // Update message
    const updatedMessage = { ...entry.message, ...updates }
    const newSize = this.calculateMessageSize(updatedMessage)
    
    // Update memory usage
    this.memoryUsage = this.memoryUsage - entry.size + newSize

    // Update entry
    entry.message = updatedMessage
    entry.size = newSize
    entry.timestamp = Date.now()

    return true
  }

  // Get cache statistics
  getStats(): CacheStats {
    let totalMessages = 0
    let oldestMessage: Date | null = null
    let newestMessage: Date | null = null

    this.cache.forEach(conversationCache => {
      conversationCache.forEach(entry => {
        totalMessages++
        const messageDate = new Date(entry.message.createdAt)
        
        if (!oldestMessage || messageDate < oldestMessage) {
          oldestMessage = messageDate
        }
        
        if (!newestMessage || messageDate > newestMessage) {
          newestMessage = messageDate
        }
      })
    })

    return {
      totalMessages,
      totalMemoryUsage: this.memoryUsage,
      oldestMessage,
      newestMessage,
      conversationCount: this.cache.size
    }
  }

  // Check if cache needs cleanup
  private checkAndCleanup(): void {
    const stats = this.getStats()
    
    // Check memory usage
    if (this.memoryUsage > this.config.maxMemoryUsage) {
      this.cleanupByMemory()
    }
    
    // Check message count
    if (stats.totalMessages > this.config.maxMessages) {
      this.cleanupByCount()
    }
  }

  // Cleanup by memory usage
  private cleanupByMemory(): void {
    const targetMemory = this.config.maxMemoryUsage * 0.8 // Clean to 80% of max
    
    // Collect all entries with their age
    const allEntries: Array<{
      conversationId: string
      messageId: string
      entry: CacheEntry
      age: number
    }> = []

    this.cache.forEach((conversationCache, conversationId) => {
      conversationCache.forEach((entry, messageId) => {
        allEntries.push({
          conversationId,
          messageId,
          entry,
          age: Date.now() - entry.timestamp
        })
      })
    })

    // Sort by age (oldest first)
    allEntries.sort((a, b) => b.age - a.age)

    // Remove oldest entries until we reach target memory
    while (this.memoryUsage > targetMemory && allEntries.length > 0) {
      const { conversationId, messageId } = allEntries.pop()!
      this.removeMessage(conversationId, messageId)
    }
  }

  // Cleanup by message count
  private cleanupByCount(): void {
    const targetCount = Math.floor(this.config.maxMessages * 0.8) // Clean to 80% of max
    const stats = this.getStats()
    
    if (stats.totalMessages <= targetCount) return

    // Collect all entries with their age
    const allEntries: Array<{
      conversationId: string
      messageId: string
      entry: CacheEntry
      age: number
    }> = []

    this.cache.forEach((conversationCache, conversationId) => {
      conversationCache.forEach((entry, messageId) => {
        allEntries.push({
          conversationId,
          messageId,
          entry,
          age: Date.now() - entry.timestamp
        })
      })
    })

    // Sort by age (oldest first)
    allEntries.sort((a, b) => b.age - a.age)

    // Remove oldest entries until we reach target count
    const toRemove = stats.totalMessages - targetCount
    for (let i = 0; i < toRemove && i < allEntries.length; i++) {
      const { conversationId, messageId } = allEntries[i]
      this.removeMessage(conversationId, messageId)
    }
  }

  // Cleanup expired messages
  private cleanupExpired(): void {
    const now = Date.now()
    const expiredEntries: Array<{ conversationId: string; messageId: string }> = []

    this.cache.forEach((conversationCache, conversationId) => {
      conversationCache.forEach((entry, messageId) => {
        if (now - entry.timestamp > this.config.maxAge) {
          expiredEntries.push({ conversationId, messageId })
        }
      })
    })

    expiredEntries.forEach(({ conversationId, messageId }) => {
      this.removeMessage(conversationId, messageId)
    })
  }

  // Check if entry is expired
  private isExpired(entry: CacheEntry): boolean {
    return Date.now() - entry.timestamp > this.config.maxAge
  }

  // Calculate message size in bytes
  private calculateMessageSize(message: Message): number {
    // Rough estimation of message size in memory
    let size = 0
    
    // Base message properties
    size += JSON.stringify(message).length * 2 // UTF-16 encoding
    
    // Add extra for object overhead
    size += 100
    
    // Add size for content based on type
    if (message.content.text) {
      size += message.content.text.length * 2
    }
    
    if (message.content.imageUrl) {
      size += message.content.imageUrl.length * 2
    }
    
    if (message.content.fileUrl) {
      size += message.content.fileUrl.length * 2
    }
    
    return size
  }

  // Start cleanup timer
  private startCleanupTimer(): void {
    if (this.cleanupTimer) {
      clearInterval(this.cleanupTimer)
    }

    this.cleanupTimer = window.setInterval(() => {
      this.cleanupExpired()
    }, this.config.cleanupInterval)
  }

  // Stop cleanup timer
  private stopCleanupTimer(): void {
    if (this.cleanupTimer) {
      clearInterval(this.cleanupTimer)
      this.cleanupTimer = null
    }
  }

  // Destroy cache manager
  destroy(): void {
    this.stopCleanupTimer()
    this.clearAll()
  }
}

// Create singleton instance
export const messageCache = new MessageCacheManager()

// Export for testing or custom instances
export { MessageCacheManager }